package jdos;

import jdos.cpu.*;
import jdos.cpu.Callback.Handler;
import jdos.debug.Debug;
import jdos.gui.Main;
import jdos.gui.Mapper;
import jdos.hardware.*;
import jdos.hardware.VGA;
import jdos.ints.*;
import jdos.misc.Log;
import jdos.misc.setup.*;

public class Dosbox {
	static public boolean allPrivileges = true;

	public static final boolean applet = false;

	private static /*Bit32u*/long ticksRemain;
	private static /*Bit32u*/long ticksLast;
	private static /*Bit32u*/long ticksAdded;
	public static /*Bit32s*/int ticksDone;
	public static /*Bit32u*/long ticksScheduled;
	public static boolean ticksLocked;

	public static /*Bitu*/int Normal_Loop() {
		/*Bits*/int ret;
		while (true) {
			if (Pic.PIC_RunQueue()) {
				ret = CPU.cpudecoder.call();
				if (ret < 0) return 1;
				if (ret > 0) {
					//if (ret >= Callback.CB_MAX) return 0;
					try {
						Callback.inHandler++;
						//                            if (ret != 15 && ret !=7 && ret != 19 && ret != 16)
						//                               System.out.println(Callback.CallBack_Handlers[ret].getName());
						Handler h = Callback.CallBack_Handlers[ret];
						/*Bitu*/int blah = h.call();
						if (blah != 0) return blah;
					} catch (PageFaultException e) {
						if (e.shouldRunException) {
							Paging.pageFault = true;
							CPU.CPU_Exception(CPU.cpu.exception.which, CPU.cpu.exception.error);
							Paging.pageFault = false;
						}
						Log.exit("This should not happen");
					} finally {
						Callback.inHandler--;
					}
				}
				if (Config.C_DEBUG) if (Debug.DEBUG_ExitLoop()) return 0;
			} else {
				Main.GFX_Events();
				if (ticksRemain > 0) {
					Timer.TIMER_AddTick();
					ticksRemain--;
				} else {
					//goto increaseticks;
					break;
				}
			}
		}
		//increaseticks:
		if (ticksLocked) {
			ticksRemain = 5;
			/* Reset any auto cycle guessing for this frame */
			ticksLast = Main.GetTicks();
			ticksAdded = 0;
			ticksDone = 0;
			ticksScheduled = 0;
		} else {
			/*Bit32u*/long ticksNew;
			ticksNew = Main.GetTicks();
			ticksScheduled += ticksAdded;
			if (ticksNew > ticksLast) {
				ticksRemain = ticksNew - ticksLast;
				ticksLast = ticksNew;
				ticksDone += ticksRemain;
				if (ticksRemain > 20) {
					ticksRemain = 20;
				}
				ticksAdded = ticksRemain;
				if (CPU.CPU_CycleAutoAdjust && !CPU.CPU_SkipCycleAutoAdjust) {
					if (ticksScheduled >= 250 || ticksDone >= 250 || (ticksAdded > 15 && ticksScheduled >= 5)) {
						if (ticksDone < 1) ticksDone = 1;
						// Protect against div by zero
						/* ratio we are aiming for is around 90% usage*/
						/*Bit32s*/int ratio = (int) ((ticksScheduled * (CPU.CPU_CyclePercUsed * 90 * 1024 / 100 / 100)) / ticksDone);
						/*Bit32s*/int new_cmax = CPU.CPU_CycleMax;
						/*Bit64s*/long cproc = (/*Bit64s*/long) CPU.CPU_CycleMax * (/*Bit64s*/long) ticksScheduled;
						if (cproc > 0) {
							/* ignore the cycles added due to the io delay code in order
							   to have smoother auto cycle adjustments */
							double ratioremoved = (double) CPU.CPU_IODelayRemoved / (double) cproc;
							if (ratioremoved < 1.0) {
								ratio = (/*Bit32s*/int) ((double) ratio * (1 - ratioremoved));
								/* Don't allow very high ratio which can cause us to lock as we don't scale down
								 * for very low ratios. High ratio might result because of timing resolution */
								if (ticksScheduled >= 250 && ticksDone < 10 && ratio > 20480) ratio = 20480;
								/*Bit64s*/long cmax_scaled = (/*Bit64s*/long) CPU.CPU_CycleMax * (/*Bit64s*/long) ratio;
								if (ratio <= 1024) new_cmax = (/*Bit32s*/int) (cmax_scaled / (/*Bit64s*/long) 1024);
								else new_cmax = (/*Bit32s*/int) (1 + (CPU.CPU_CycleMax >> 1) + cmax_scaled / (/*Bit64s*/long) 2048);
							}
						}

						if (new_cmax < CPU.CPU_CYCLES_LOWER_LIMIT) new_cmax = CPU.CPU_CYCLES_LOWER_LIMIT;

						/* ratios below 1% are considered to be dropouts due to
						   temporary load imbalance, the cycles adjusting is skipped */
						if (ratio > 10) {
							/* ratios below 12% along with a large time since the last update
							   has taken place are most likely caused by heavy load through a
							   different application, the cycles adjusting is skipped as well */
							if ((ratio > 120) || (ticksDone < 700)) {
								CPU.CPU_CycleMax = new_cmax;
								if (CPU.CPU_CycleLimit > 0) {
									if (CPU.CPU_CycleMax > CPU.CPU_CycleLimit) CPU.CPU_CycleMax = CPU.CPU_CycleLimit;
								}
							}
						}
						CPU.CPU_IODelayRemoved = 0;
						ticksDone = 0;
						ticksScheduled = 0;
					} else if (ticksAdded > 15) {
						/* ticksAdded > 15 but ticksScheduled < 5, lower the cycles
						   but do not reset the scheduled/done ticks to take them into
						   account during the next auto cycle adjustment */
						CPU.CPU_CycleMax /= 3;
						if (CPU.CPU_CycleMax < CPU.CPU_CYCLES_LOWER_LIMIT) CPU.CPU_CycleMax = CPU.CPU_CYCLES_LOWER_LIMIT;
					}
				}
			} else {
				ticksAdded = 0;
				Main.Delay(1);
				ticksDone -= Main.GetTicks() - ticksNew;
				if (ticksDone < 0) ticksDone = 0;
			}
		}
		return 0;
	}

	static public void DOSBOX_RunMachinePF() {
		/*Bitu*/int ret;
		do {
			ret = Normal_Loop();
		} while (ret == 0);
	}

	static public void DOSBOX_RunMachine() {
		/*Bitu*/int ret;
		do {
			try {
				ret = Normal_Loop();
			} catch (PageFaultException e) {
				if (e.shouldRunException) {
					Paging.pageFault = true;
					CPU.CPU_Exception(CPU.cpu.exception.which, CPU.cpu.exception.error);
					Paging.pageFault = false;
				}
				ret = 0;
			//} catch (Exception e) {
			//	ret = 0;
			}
		} while (ret == 0);
	}

	static private boolean autoadjust = false;
	static private Mapper.MAPPER_Handler DOSBOX_UnlockSpeed = new Mapper.MAPPER_Handler() {
		public void call(boolean pressed) {
			if (pressed) {
				Log.log_msg("Fast Forward ON");
				ticksLocked = true;
				if (CPU.CPU_CycleAutoAdjust) {
					autoadjust = true;
					CPU.CPU_CycleAutoAdjust = false;
					CPU.CPU_CycleMax /= 3;
					if (CPU.CPU_CycleMax < 1000) CPU.CPU_CycleMax = 1000;
				}
			} else {
				Log.log_msg("Fast Forward OFF");
				ticksLocked = false;
				if (autoadjust) {
					autoadjust = false;
					CPU.CPU_CycleAutoAdjust = true;
				}
			}
		}
	};

	public static void DOSBOX_RealInit() {
		System.out.println("DOSBOX_RealInit");
		/* Initialize some dosbox internals */

		ticksRemain = 0;
		ticksLast = Main.GetTicks();
		ticksLocked = false;

		Int10.int10 = new Int10.Int10Data();
		Int10.int10.vesa_nolfb = false;
		Int10.int10.vesa_oldvbe = false;
		VGA.VGA_Init2();
	}
}
